using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Fantasy.SourceGenerator.Common;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
#pragma warning disable CS0649 // Field is never assigned to, and will always have its default value
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable.

namespace Fantasy.SourceGenerator.Generators;
[Generator]
internal partial class NetworkProtocolGenerator : IIncrementalGenerator
{
    public void Initialize(IncrementalGeneratorInitializationContext context)
    {
        var networkProtocols = context.SyntaxProvider
            .CreateSyntaxProvider(
                predicate: static (node, _) => IsNetworkProtocolClass(node),
                transform: static (ctx, _) => GetNetworkProtocolInfo(ctx))
            .Where(static info => info != null)
            .Collect();
       
        var compilationAndTypes = context.CompilationProvider.Combine(networkProtocols);
       
        context.RegisterSourceOutput(compilationAndTypes, static (spc, source) =>
        {
            if (CompilationHelper.IsSourceGeneratorDisabled(source.Left))
            {
                return;
            }

            if (!CompilationHelper.HasFantasyDefine(source.Left))
            {
                return;
            }

            if (source.Left.GetTypeByMetadataName("Fantasy.Assembly.INetworkProtocolRegistrar") == null)
            {
                return;
            }

            GenerateCode(spc, source.Left, source.Right!);
        });
    }

    #region GenerateCode

    private static void GenerateCode(
        SourceProductionContext context,
        Compilation compilation,
        IEnumerable<NetworkProtocolTypeInfo> networkProtocolTypeInfos)
    {
        var networkProtocolTypeInfoList = networkProtocolTypeInfos.ToList();
        // 生成网络网络协议类型注册的类。
        GenerateNetworkProtocolTypesCode(context, compilation, networkProtocolTypeInfoList);
        // 生成OpCode注册方法。
        GenerateOpCodeRegistrarCode(context, compilation, networkProtocolTypeInfoList);
        // 生成Request消息的ResponseType辅助方法。
        GenerateResponseTypeRegistrarCode(context, compilation, networkProtocolTypeInfoList);
    }
    
    private static void GenerateNetworkProtocolTypesCode(
        SourceProductionContext context,
        Compilation compilation, 
        List<NetworkProtocolTypeInfo> networkProtocolTypeInfoList)
    {
        var markerClassName = compilation.GetAssemblyName("NetworkProtocolRegistrar", out var assemblyName, out _);
        var builder = new SourceCodeBuilder();
        // 添加文件头
        builder.AppendLine(GeneratorConstants.AutoGeneratedHeader);
        // 添加 using
        builder.AddUsings(
            "System",
            "System.Collections.Generic",
            "Fantasy.Assembly",
            "Fantasy.DataStructure.Collection"
        );
        // 开始命名空间
        builder.BeginDefaultNamespace();
		// 开始类定义（实现 INetworkProtocolRegistrar 接口）
		builder.AddXmlComment($"Auto-generated NetworkProtocol registration class for {assemblyName}");
        builder.BeginClass(markerClassName, "internal sealed", "global::Fantasy.Assembly.INetworkProtocolRegistrar");
        builder.BeginMethod("public List<Type> GetNetworkProtocolTypes()");
        try
        {
            if (networkProtocolTypeInfoList.Any())
            {
                builder.AppendLine($"return new List<Type>({networkProtocolTypeInfoList.Count})");
                builder.AppendLine("{");
                builder.Indent();
                foreach (var system in networkProtocolTypeInfoList)
                {
                    builder.AppendLine($"typeof({system.FullName}),");
                }
                builder.Unindent();
                builder.AppendLine("};");
                builder.AppendLine();
            }
            else
            {
                builder.AppendLine($"return new List<Type>();");
            }
        }
        catch (Exception e)
        {
            Console.WriteLine(e);
            throw;
        }
        builder.EndMethod();
        builder.AppendLine();
        // 结束类和命名空间
        builder.EndClass();
        builder.EndNamespace();
        // 输出源代码
        context.AddSource($"{markerClassName}.g.cs", builder.ToString());
    }
    
    private static void GenerateOpCodeRegistrarCode(
        SourceProductionContext context,
        Compilation compilation, 
        List<NetworkProtocolTypeInfo> networkProtocolTypeInfoList)
    {
        var markerClassName = compilation.GetAssemblyName("OpCodeRegistrar", out var assemblyName, out _);
        var routeTypeInfos = networkProtocolTypeInfoList.Where(d => d.RouteType.HasValue).ToList();
        var builder = new SourceCodeBuilder();
        // 添加文件头
        builder.AppendLine(GeneratorConstants.AutoGeneratedHeader);
        // 添加 using
        builder.AddUsings(
            "System",
            "System.Collections.Generic",
            "Fantasy.Assembly",
            "Fantasy.DataStructure.Collection",
            "System.Runtime.CompilerServices"
        );
        builder.AppendLine();
        // 开始命名空间
         builder.BeginDefaultNamespace();
        // 开始类定义（实现 IOpCodeRegistrar 接口）
        builder.AddXmlComment($"Auto-generated OpCodeRegistrar class for {assemblyName}");
        builder.BeginClass(markerClassName, "internal sealed", "global::Fantasy.Assembly.IOpCodeRegistrar");
        // 开始定义OpCodes方法
        builder.AddXmlComment("TypeOpCodes");
        builder.BeginMethod("public uint[] TypeOpCodes()");
        
        try
        {
            if (networkProtocolTypeInfoList.Any())
            {
                builder.AppendLine($"var uintArray = new uint[{networkProtocolTypeInfoList.Count}];");
                for (var i = 0; i < networkProtocolTypeInfoList.Count; i++)
                {
                    builder.AppendLine($"uintArray[{i}] = {networkProtocolTypeInfoList[i].OpCode};");
                }
                builder.AppendLine("return uintArray;");
            }
            else
            {
                builder.AppendLine("return Array.Empty<uint>();");
            }
        }
        catch (Exception e)
        {
            Console.WriteLine(e);
            throw;
        }
        
        builder.EndMethod();
        // 开始定义OpCodeTypes方法
        builder.AddXmlComment("OpCodeTypes");
        builder.BeginMethod("public global::System.Type[] OpCodeTypes()");
        
        try
        {
            if (networkProtocolTypeInfoList.Any())
            {
                builder.AppendLine($"var typeArray = new global::System.Type[{networkProtocolTypeInfoList.Count}];");
                for (var i = 0; i < networkProtocolTypeInfoList.Count; i++)
                {
                    builder.AppendLine($"typeArray[{i}] = typeof({networkProtocolTypeInfoList[i].FullName});");
                }
                builder.AppendLine("return typeArray;");
            }
            else
            {
                builder.AppendLine("return Array.Empty<global::System.Type>();");
            }
        }
        catch (Exception e)
        {
            Console.WriteLine(e);
            throw;
        }
        
        builder.EndMethod();
        // 开始定义CustomRouteTypeOpCodes方法
        builder.AddXmlComment("CustomRouteTypeOpCodes");
        builder.BeginMethod("public uint[] CustomRouteTypeOpCodes()");
        
        try
        {
            if (routeTypeInfos.Any())
            {
                builder.AppendLine($"var unitArray = new uint[{routeTypeInfos.Count}];");
                for (var i = 0; i < routeTypeInfos.Count; i++)
                {
                    builder.AppendLine($"unitArray[{i}] = {routeTypeInfos[i].OpCode};");
                }
                builder.AppendLine("return unitArray;");
            }
            else
            {
                builder.AppendLine("return Array.Empty<uint>();");
            }
        }
        catch (Exception e)
        {
            Console.WriteLine(e);
            throw;
        }
        
        builder.EndMethod();
        // 开始定义CustomRouteTypeOpCodes方法
        builder.AddXmlComment("CustomRouteTypes");
        builder.BeginMethod("public int[] CustomRouteTypes()");
        
        try
        {
            if (routeTypeInfos.Any())
            {
                builder.AppendLine($"var intArray = new int[{routeTypeInfos.Count}];");
                for (var i = 0; i < routeTypeInfos.Count; i++)
                {
                    builder.AppendLine($"intArray[{i}] = {routeTypeInfos[i].RouteType};");
                }
                builder.AppendLine("return intArray;");
            }
            else
            {
                builder.AppendLine("return Array.Empty<int>();");
            }
        }
        catch (Exception e)
        {
            Console.WriteLine(e);
            throw;
        }
        
        builder.EndMethod();
        // 结束类和命名空间
        builder.EndClass();
        builder.EndNamespace();
        // 输出源代码
        context.AddSource($"{markerClassName}.g.cs", builder.ToString());
    }

    private static void GenerateResponseTypeRegistrarCode(
        SourceProductionContext context,
        Compilation compilation, 
        List<NetworkProtocolTypeInfo> networkProtocolTypeInfoList)
    {
        var markerClassName = compilation.GetAssemblyName("ResponseTypeRegistrar", out var assemblyName, out _);
        var requestList = networkProtocolTypeInfoList.Where(d => d.ResponseType != null).ToList();
        var builder = new SourceCodeBuilder();
        // 添加文件头
        builder.AppendLine(GeneratorConstants.AutoGeneratedHeader);
        // 添加 using
        builder.AddUsings(
            "System",
            "System.Collections.Generic",
            "Fantasy.Assembly",
            "Fantasy.DataStructure.Collection",
            "System.Runtime.CompilerServices"
        );
        builder.AppendLine();
        // 开始命名空间
         builder.BeginDefaultNamespace();
        // 开始类定义（实现 IResponseTypeRegistrar 接口）
        builder.AddXmlComment($"Auto-generated ResponseTypeRegistrar class for {assemblyName}");
        builder.BeginClass(markerClassName, "internal sealed", "global::Fantasy.Assembly.IResponseTypeRegistrar");
        // 开始定义OpCodes方法
        builder.AddXmlComment("OpCodes");
        builder.BeginMethod("public uint[] OpCodes()");
        try
        {
            if (requestList.Any())
            {
                builder.AppendLine($"var unitArray = new uint[{requestList.Count}];");
                for (var i = 0; i < requestList.Count; i++)
                {
                    builder.AppendLine($"unitArray[{i}] = {requestList[i].OpCode};");
                }
                builder.AppendLine("return unitArray;");
            }
            else
            {
                builder.AppendLine("return Array.Empty<uint>();");
            }
        }
        catch (Exception e)
        {
            Console.WriteLine(e);
            throw;
        }
        builder.EndMethod();
        // 开始定义Types方法
        builder.AddXmlComment("Types");
        builder.BeginMethod("public global::System.Type[] Types()");
        try
        {
            if (requestList.Any())
            {
                builder.AppendLine($"var typeArray = new global::System.Type[{requestList.Count}];");
                for (var i = 0; i < requestList.Count; i++)
                {
                    builder.AppendLine($"typeArray[{i}] = typeof({requestList[i].ResponseType});");
                }
                builder.AppendLine("return typeArray;");
            }
            else
            {
                builder.AppendLine("return Array.Empty<global::System.Type>();");
            }
        }
        catch (Exception e)
        {
            Console.WriteLine(e);
            throw;
        }
        builder.EndMethod();
        // 结束类和命名空间
        builder.EndClass();
        builder.EndNamespace();
        // 输出源代码
        context.AddSource($"{markerClassName}.g.cs", builder.ToString());
    }

    #endregion
    
    private static NetworkProtocolTypeInfo? GetNetworkProtocolInfo(GeneratorSyntaxContext context)
    {
        var classDecl = (ClassDeclarationSyntax)context.Node;
        var symbol = context.SemanticModel.GetDeclaredSymbol(classDecl) as INamedTypeSymbol;

        if (symbol == null || !symbol.IsInstantiable())
        {
            return null;
        }

        var baseType = symbol.BaseType;

        if (baseType == null)
        {
            return null;
        }

        if (baseType.ToDisplayString() != "Fantasy.Network.Interface.AMessage")
        {
            return null;
        }

        // 获取 OpCode 方法的值
        uint? opCodeValue = null;
        var opCodeMethod = symbol.GetMembers("OpCode").OfType<IMethodSymbol>().FirstOrDefault();
        var opCodeSyntax = opCodeMethod?.DeclaringSyntaxReferences.FirstOrDefault()?.GetSyntax() as MethodDeclarationSyntax;

        if (opCodeSyntax?.Body != null)
        {
            var returnStatement = opCodeSyntax.Body.DescendantNodes()
                .OfType<ReturnStatementSyntax>()
                .FirstOrDefault();

            if (returnStatement?.Expression != null)
            {
                var constantValue = context.SemanticModel.GetConstantValue(returnStatement.Expression);
                if (constantValue.HasValue && constantValue.Value is uint uintValue)
                {
                    opCodeValue = uintValue;
                }
            }
        }

        if (!opCodeValue.HasValue)
        {
            return null;
        }

        // 获取 ResponseType 属性及其类型
        string? responseTypeName = null;
        var responseTypeProperty = symbol.GetMembers("ResponseType").OfType<IPropertySymbol>().FirstOrDefault();
        if (responseTypeProperty != null)
        {
            // 获取 ResponseType 属性的类型（例如 G2C_TestResponse）
            responseTypeName = responseTypeProperty.Type.GetFullName();
        }
        
        // 获取 RouteType 属性的值
        int? routeTypeValue = null;
        var routeTypeProperty = symbol.GetMembers("RouteType").OfType<IPropertySymbol>().FirstOrDefault();
        var routeTypeSyntax = routeTypeProperty?.DeclaringSyntaxReferences.FirstOrDefault()?.GetSyntax() as PropertyDeclarationSyntax;
        if (routeTypeSyntax?.ExpressionBody != null)
        {
            var constantValue = context.SemanticModel.GetConstantValue(routeTypeSyntax.ExpressionBody.Expression);
            if (constantValue.HasValue && constantValue.Value is int intValue)
            {
                routeTypeValue = intValue;
            }
        }

        return new NetworkProtocolTypeInfo(
            symbol.GetFullName(),
            opCodeValue.Value,
            responseTypeName,
            routeTypeValue
        );
    }

    private static bool IsNetworkProtocolClass(SyntaxNode node)
    {
        if (node is not ClassDeclarationSyntax classDecl)
        {
            return false;
        }

        if (classDecl.BaseList == null)
        {
            return false;
        }
        
        foreach (var baseTypeSyntax in classDecl.BaseList.Types)
        {
            if (baseTypeSyntax.Type.ToString().Contains("AMessage"))
            {
                return true;
            }
        }
        
        return false;
    }

    private sealed record NetworkProtocolTypeInfo(
        string FullName,
        uint OpCode,
        string? ResponseType,
        int? RouteType);
}